﻿using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace FanaticToy.Core.CosntructorCloner
{
    public class Cloner
    {
        public string GetCloneConstrcutor(string filePath)
        {
            if (filePath.EndsWith(".cs") == false) return null;
            var text = File.ReadAllText(filePath);
            var tree = CSharpSyntaxTree.ParseText(text);

            var classDeclare = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault();

            if (classDeclare == null) return null;

            var propertyDeclares = classDeclare.DescendantNodes().OfType<PropertyDeclarationSyntax>();

            if (propertyDeclares == null) return null;
            if (propertyDeclares.Count() == 0) return null;

            var sb = new StringBuilder();

            var className = classDeclare.Identifier.Text;

            sb.Append($"public {className}({className} other)");

            if (HasValidBaseType(classDeclare))
            {
                sb.Append(":base(other)");
            }

            sb.AppendLine();
            sb.AppendLine($"{{");
            foreach (var propDecleare in propertyDeclares)
            {
                if (propDecleare.Modifiers
                  .Any(i =>
                  i.IsKind(SyntaxKind.PrivateKeyword) ||
                  i.IsKind(SyntaxKind.OverrideKeyword) ||
                  i.IsKind(SyntaxKind.StaticKeyword)))
                    continue;

                AppendSetExpression(sb, propDecleare);
            }
            sb.AppendLine($"}}");
            return sb.ToString();

        }

        private static void AppendSetExpression(StringBuilder sb, PropertyDeclarationSyntax propDecleare)
        {
            string propName = GetPropertyActualMember(propDecleare);
            sb.AppendLine($"{propName} = other.{propName};");
        }

        private static string GetPropertyActualMember(PropertyDeclarationSyntax propDecleare)
        {
            var propName = propDecleare.Identifier.Text;
            var accessorList = propDecleare.AccessorList;
            if (accessorList == null) return propName;
            var accessors = accessorList.DescendantNodes().OfType<AccessorDeclarationSyntax>();

            var setAccessor = accessors.FirstOrDefault(n => n.IsKind(SyntaxKind.SetAccessorDeclaration));
            var getAccessor = accessors.FirstOrDefault(n => n.IsKind(SyntaxKind.GetAccessorDeclaration));
            if (getAccessor == null) return propName;
            var matchedFieldName = getAccessor
                .DescendantNodes()
                .OfType<IdentifierNameSyntax>()
                .Where(i => IsPropertyMatchedFieldName(propName, i.ToString()))
                .FirstOrDefault();

            if (matchedFieldName == null) return propName;
            return matchedFieldName.ToString();

            return propName;
        }

        private bool HasValidBaseType(ClassDeclarationSyntax classDeclare)
        {
            var baseList = classDeclare.BaseList;
            if (baseList == null) return false;
            var sourceText = baseList.ToString();
            var sourceParts = sourceText.Split(',');
            var classCounter = 0;

            foreach (var sourcePart in sourceParts)
            {
                if (IsInterface(sourcePart)) continue;
                classCounter++;
            }

            return classCounter == 1;
        }

        private bool IsInterface(string typeName)
        {
            var nameParts = typeName.Split('.');
            return nameParts.LastOrDefault().FirstOrDefault() == 'I';
        }

        private static bool IsPropertyMatchedFieldName(string propertyName, string idName)
        {
            if (idName.StartsWith("_"))
            {
                idName = idName.Substring(1);
            }

            if (string.IsNullOrWhiteSpace(idName)) return false;
            if (char.IsUpper(idName[0])) return false;
            return string.Equals(propertyName, idName, System.StringComparison.OrdinalIgnoreCase);
        }

    }
}
